﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Data.OleDb;
using System.Data;
using System.Windows;
using Quiz.LINQData;
using SQDebugging;

namespace Quiz
{
    class DataBase : IDisposable
    {

        #region Member
        private readonly string _connectionString;
        private readonly DataContext _db;
        private Table<Frage> _fragen;
        private Table<Kategorie> _kategorien;
        private Table<Ersteller> _ersteller;
        private Table<Antwort> _antworten;
        private Table<Optionen> _optionen;
        #endregion

        #region Properties
        public string ConnectionString
        {
            get { return _connectionString; }
        }

        /// <summary>
        /// Zugriff auf den DataContext
        /// </summary>
        public DataContext Db
        {
            get { return _db; }
        }

        /// <summary>
        /// Auflistung der Fragen für die LINQ2SQL Abfrage
        /// </summary>
        public Table<Frage> Fragen
        {
            get { return _fragen; }
            set { _fragen = value; }
        }

        /// <summary>
        /// Auflistung aller Kategorien die im System hinterlegt sind.
        /// </summary>
        public Table<Kategorie> Kategorien
        {
            get { return _kategorien; }
            set { _kategorien = value; }
        }

        /// <summary>
        /// Auflistung der Fragen für die LINQ2SQL Abfrage
        /// </summary>
        public Table<Ersteller> Erstellers
        {
            get { return _ersteller; }
            set { _ersteller = value; }
        }

        /// <summary>
        /// Auflistung aller im System hinterlegten antworten
        /// </summary>
        public Table<Antwort> Antworten
        {
            get { return _antworten; }
            set { _antworten = value; }
        }

        /// <summary>
        /// Eine Auflistung aller zur Verfügung stehenden Optionen
        /// </summary>
        public Table<Optionen> Optionen
        {
            get { return _optionen; }
            set { _optionen = value; }
        }

        #endregion

        #region Konstruktor
        public DataBase()
        {
            //der Connectionstring für die SQL-Compact Database zusammenbauen
            string programmPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "QuizDB.sdf");
            _connectionString = "Data Source=" + programmPath + ";Persist Security Info=False;";
            _db = new DataContext(_connectionString);

            //Die Fragen aus der DB laden, da es sich hier um LinQ handelt werden alle restlichen Klassen automatisch mit "gefüllt"
            _fragen = _db.GetTable<Frage>();
            _kategorien = _db.GetTable<Kategorie>();
            _ersteller = _db.GetTable<Ersteller>();
            _antworten = _db.GetTable<Antwort>();
            _optionen = _db.GetTable<Optionen>();
          
        }
        #endregion

        #region Functions/subs

        /// <summary>
        /// Fragen aus einem Excel-File 2003/2007 importieren.
        /// </summary>
        /// <param name="dateiNameP">Pfad inkl. Dateiname z.B. "C:\Temp\test.xlsx"</param>
        /// <returns>TRUE-Import erfolgreich| FALSE->Import fehlgeschlagen</returns>
        public Boolean FragenImportieren(string dateiNameP)
        {
            try
            {
                //Erst prüfen um welchen ExcelTyp es sich handelt, xls (2003) oder xlsx (2007)
                string connectionString = string.Empty;
                string dateiTyp = System.IO.Path.GetExtension(dateiNameP).Trim('.');

                //Den jeweiligen passenden Connectionstring für die richtige Excelformat zusammenbauen.
                switch (dateiTyp.ToLower())
                {
                    case "xls":
                        connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; Data Source= {0};Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\"", dateiNameP);
                        break;
                    case "xlsx":
                        connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0; Data Source= {0};Extended Properties=\"Excel 12.0 Xml;HDR=YES\"", dateiNameP);
                        break;
                }

                //Es müssen sich die Daten im Sheet mit dem Namen "Tabelle1" befinden.
                OleDbDataAdapter dataAdapter = new OleDbDataAdapter("SELECT * FROM [Tabelle1$]", connectionString);
                DataSet myDataSet = new DataSet();
                dataAdapter.Fill(myDataSet, "Tabelle");
                DataTable dataTable = myDataSet.Tables["Tabelle"];

                //Linq Ausdruck zusammenbauen, der eine "neues" Objekt erstellt in dem alle meine Zeilen enthalten sind.
                var query = from tabellenZeile in dataTable.AsEnumerable()
                            select new
                            {
                                //Erstellen eines "FrageObjektes" mit dem man eine neue Frage erstellen kann.
                                Fragetext = tabellenZeile.Field<string>("Frage"),
                                RichtigeAntwort = tabellenZeile.Field<string>("richtige Antwort"),
                                FalscheAntwort1 = tabellenZeile.Field<string>("falsche Antwort I"),
                                FalscheAntwort2 = tabellenZeile.Field<string>("falsche Antwort II"),
                                Ersteller = tabellenZeile.Field<string>("Verfasser"),
                                KategorieBezeichnung = tabellenZeile.Field<string>("Kategorie"),
                            };

                //Alle gefundenen Zeilen durchgehen und die Fragen hinzufügen.
                foreach (var frage in query)
                {
                    //Prüfen ob überall ein Wert hinterlegt wurde,
                    if (frage.Fragetext != null && frage.Ersteller != null && frage.FalscheAntwort1 != null && frage.FalscheAntwort2 != null && frage.KategorieBezeichnung != null && frage.RichtigeAntwort != null)
                    {
                        //Nur wenn auch ein Fragetext und alle Antworten angegeben wurde, die Frage auch hinzufügen
                        if (frage.Fragetext.Trim().Length > 0 && frage.RichtigeAntwort.Trim().Length > 0 && frage.FalscheAntwort1.Trim().Length > 0 && frage.FalscheAntwort2.Trim().Length > 0)
                        {
                            //Prüfen ob die gleiche Frage bereits hinzugeüfügt wurde anhand, des Fragetextes
                            var queryFrage = from qfrage in Fragen where qfrage.Fragetext == frage.Fragetext select qfrage;
                            Boolean existiert = false;
                            foreach (Frage eFrage in queryFrage)
                            {
                                existiert = true;
                            }

                            //Wenn die Frage noch nicht existiert, dann die Frage hinzufügen.
                            if (!existiert)
                            {

                                Kategorie neueKat = new Kategorie();
                                neueKat.KategorieBezeichnung = frage.KategorieBezeichnung.Trim();
                                neueKat = KategorieHinzufuegen(neueKat);

                                if (neueKat != null)
                                {
                                    List<Antwort> antworten = new List<Antwort>();
                                    Antwort antwort = new Antwort();
                                    antwort.Antworttext = frage.FalscheAntwort1.Trim();
                                    antwort.Richtig = false;
                                    antworten.Add(antwort);

                                    antwort = new Antwort();
                                    antwort.Antworttext = frage.FalscheAntwort2.Trim();
                                    antwort.Richtig = false;
                                    antworten.Add(antwort);

                                    antwort = new Antwort();
                                    antwort.Antworttext = frage.RichtigeAntwort.Trim();
                                    antwort.Richtig = true;
                                    antworten.Add(antwort);

                                    FrageHinzufuegenKomplett(frage.Ersteller.Trim(), frage.Fragetext.Trim(), neueKat, antworten);
                                }
                            }
                        }
                    }
                }

                dataAdapter.Dispose();
                myDataSet.Dispose();

                return true;
            }
            catch (Exception ex)
            {
                //Hier den kompletten Stacktrace als Messagebox ausgeben, wenn ein Fehler beim Iportieren aufgetreten ist.
                MessageBox.Show(Debugging.GetStackTrace(ex));
                Debugging.WriteLog(ex);
                return false;
            }
            
        }

        /// <summary>
        /// Löschen einer Frage inkl. aller relevaten zusatzinformationen.
        /// Wenn der Kontakt nirgens weiter verknüpft ist, wird auch dieser aus der Liste entfernt.
        /// </summary>
        /// <param name="frageP">Die Frage die aus dem System gelöscht werden soll</param>
        public Boolean Frageloeschen(Frage frageP)
        {
            try
            {
                //Um eine Frage löschen zu können müssen erst alle Antworten gelöscht werden, dann kann die Frage gelöscht werden und
                //dann kann überprüft werden ob die enthaltene KontaktID noch bei einem anderen Datensatz vorkommt und wenn nicht
                //kann auch diese aus der Liste entfernt werden. Gleiches gilt für die KategorieID.


                //ANTWORTEN LÖSCHEN
                //Alle Antworten heraussuchen die gelöscht werden sollen
                var queryAntworten = from antwort in frageP.Antworten select antwort;
                Antworten.DeleteAllOnSubmit(queryAntworten);
                //Um eine Änderung zu speichern. muss nur SubmitChanges aufgerufen werden.
                _db.SubmitChanges();

                //Die ErstellerID und KategorieID merken
                int erstellerID = frageP.ErstellerID;
                int kategorieID = frageP.KategorieID;


                //FRAGE LÖSCHEN
                //Die Frage heraussuchen die gelöscht werden soll.
                var queryFrage = from frage in Fragen where frage.FrageID == frageP.FrageID select frage;
                Fragen.DeleteOnSubmit(queryFrage.First());
                _db.SubmitChanges();


                //ERSTELLER LÖSCHEN
                //Prüfen ob die ErstellerID noch in der Liste der Fragen vorkommt, wenn nicht diese auch löschen
                var queryErstellerIDinFragen = from frage in Fragen where frage.ErstellerID == erstellerID select frage;
                Boolean existiert = false;

                foreach (Frage eFrage in queryErstellerIDinFragen)
                {
                    existiert = true;
                }


                //Wenn die ID nicht mehr verwendet wird, diese aus der Liste entfernen.
                if (!existiert)
                {
                    //Den Ersteller heraussuchen der bei keiner anderne Frage mehr genutzt wird und löschen.
                    var queryErsteller = from ersteller in Erstellers where ersteller.ErstellerID == erstellerID select ersteller;
                    Erstellers.DeleteOnSubmit(queryErsteller.First());
                    _db.SubmitChanges();
                }


                //KATEGORIE LÖSCHEN
                //Prüfen ob die KategorieID noch in den Fragen vorkommt, wenn nicht die Kategorie auch löschen.
                var queryKategorieIDinFragen = from frage in Fragen where frage.KategorieID == kategorieID select frage;
                existiert = false;

                foreach (Frage eFrage in queryKategorieIDinFragen)
                {
                    existiert = true;
                }

                if (!existiert)
                {
                    //Die passende Kategorie heraussuchen und aus der Liste entfernen, wenn diese nicht mehr genutzt wird.
                    var queryKategorie = from kategorie in Kategorien where kategorie.KategorieID == kategorieID select kategorie;
                    Kategorien.DeleteOnSubmit(queryKategorie.First());
                    _db.SubmitChanges();
                }


                return true;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return false;
            }

        }

        /// <summary>
        /// Speichen der Änderunge an den Fragen.
        /// </summary>
        /// <returns>TRUE-> wenn das speichern geklappt hat, FALSE-> wenn das speichern nicht funktioniert hat.</returns>
        public Boolean Speichern()
        {
            try
            {
                //Um eine Änderung zu speichern. muss nur SubmitChanges aufgerufen werden.
                _db.SubmitChanges();
                return true;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return false;
            }
        }

        /// <summary>
        /// Hinzufügen einer Kompletten Frage
        /// </summary>
        /// <param name="aktuellerUserP">Der Name des aktuellen Nutzers, der für diese Frage eingetragen werden soll.</param>
        /// <param name="frageTextP">Der Fragetext</param>
        /// <param name="kategorieP">Die Kategorie für diese Frage</param>
        /// <param name="antwortenP">Eine Liste aus Antworten für diese Frage</param>
        /// <returns>Gibt die Frage zurück wenn alles geklappt hat, bei einem Fehler wird Null zurückgegeben.</returns>
        public Frage FrageHinzufuegenKomplett(string aktuellerUserP, string frageTextP, Kategorie kategorieP, List<Antwort> antwortenP)
        {
            //Beim hinzufügen von Datensätzen muss aufgepasst werden das auch alle Abhängigkeiten beachtet werden.
            try
            {
                Frage neueFrage = new Frage();
                neueFrage.ADatum = DateTime.Now;

                //Einen neuen Ersteller anlegen, mit dem übergebenen namen und dann den Ersteller Hinzufügen, im Hinzufügen wird geprüft, ob dies zulässig ist oder nicht.
                Ersteller erstellerNeu = new Ersteller();
                erstellerNeu.ErstellerFullname = aktuellerUserP;
                erstellerNeu = ErstellerHinzufuegen(erstellerNeu);

                if (erstellerNeu != null)
                {
                    neueFrage.Ersteller = erstellerNeu;
                    neueFrage.ErstellerID = erstellerNeu.ErstellerID;
                    neueFrage.Fragetext = frageTextP;
                    neueFrage.Kategorie = kategorieP;
                    neueFrage.KategorieID = kategorieP.KategorieID;
                    FrageHinzufuegen(neueFrage);

                    //Alle Antworten aus der übergebenen Collection hinzufügen, hier muss jeweils noch die richtige FrageID gesetzt werden.
                    foreach (Antwort eAntwort in antwortenP)
                    {
                        eAntwort.FrageID = neueFrage.FrageID;
                        //Der aktuellen Frage dann die Antwort hinterlegen.
                        neueFrage.Antworten.Add(eAntwort);
                    }

                    AntwortenHinzufuegen(antwortenP);

                    return neueFrage;
                }

                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Hinzufügen einer neuen Frage zur Datenbank
        /// </summary>
        public Boolean FrageHinzufuegen(Frage frageP)
        {
            try
            {
                Fragen.InsertOnSubmit(frageP);
                //Um eine Änderung zu speichern. muss nur SubmitChanges aufgerufen werden.
                _db.SubmitChanges();
                return true;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return false;
            }
        }

        /// <summary>
        /// Hinzufügen neuer antworten
        /// </summary>
        public Boolean AntwortenHinzufuegen(List<Antwort> antwortenP)
        {
            try
            {
                Antworten.InsertAllOnSubmit(antwortenP);
                //Um eine Änderung zu speichern. muss nur SubmitChanges aufgerufen werden.
                _db.SubmitChanges();
                return true;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return false;
            }
        }

        /// <summary>
        /// Hinzufügen eines neuen Erstellers, prüfen ob dieser Existiert und wenn nicht wird dieser direkt hinzugefügt.
        /// </summary>
        /// <param name="erstellerP">Neues "ErstellerObjekt"</param>
        public Ersteller ErstellerHinzufuegen(Ersteller erstellerP)
        {
            try
            {
                //Prüfen ob der aktuelle User schon existiert
                var queryUser = from ersteller in Erstellers where ersteller.ErstellerFullname.ToLower() == erstellerP.ErstellerFullname.ToLower() select ersteller;
                foreach (Ersteller eErst in queryUser)
                {
                    //Wenn der User gefunden wurde diesen zurückgeben
                    return eErst;
                }

                //Wenn der Ersteller noch nicht existiert muss dieser hier angelegt werden.
                Erstellers.InsertOnSubmit(erstellerP);
                _db.SubmitChanges();
                return erstellerP;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return null;
            }
        }

        /// <summary>
        /// Hinzufügen einer neuen Kategorie und vorher überprüfen ob diese bereits existiert.
        /// </summary>
        /// <param name="kategorieP">Neues KategorieObjekt</param>
        public Kategorie KategorieHinzufuegen(Kategorie kategorieP)
        {
            try
            {
                //Prüfen ob die Kategorie bereits existiert
                var queryKategorie = from kategorie in Kategorien where kategorie.KategorieBezeichnung.ToLower() == kategorieP.KategorieBezeichnung.ToLower() select kategorie;
                foreach (Kategorie eKategorie in queryKategorie)
                {
                    //Wenn eine Kategorie gefunden wurde diese zurückgeben
                    return eKategorie;
                }

                kategorieP.Anzeigen = true;

                //Wenn die Kategorie noch nicht existiert, wird diese hier hinzugefügt.
                Kategorien.InsertOnSubmit(kategorieP);
                _db.SubmitChanges();
                return kategorieP;
            }
            catch (Exception ex)
            {
                Debugging.WriteLog(ex);
                return null;
            }
        }

        /// <summary>
        /// Verwerfen der DB-Verbindung
        /// </summary>
        public void Dispose()
        {
            _db.Dispose();
        }
        #endregion


    }
}
